using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using static Enums.Timer;
using DG.Tweening;


public class UGUIProgress : MonoBehaviour
{
    public SuperImage BG;

    public SuperImage Progress;
    public Text Content;
    public GameObject Handler;

    public Action<PointerEventData> onDragEvt;

    Coroutine m_Coroutine;
    private TimeFormatType m_TimeType;
    private long m_TotalTime;
    private string m_Content;

    private float m_Value;
    private float m_MaxWidth;

    private TimeShowCount m_TimeShowCount;

    public float HandlerFactor = 1f;

    public float Value
    {
        get => m_Value;
        set
        {
            m_Value = value;
            Progress.fillAmount = m_Value;
            if (Handler != null)
            {
                var tmpWidth = m_MaxWidth * HandlerFactor;
                Handler.transform.SetLocalPositionX(tmpWidth * m_Value - tmpWidth / 2);
            }
        }
    }
    private Action m_SmoothCb;
    private Action m_EndCb;

    private string m_Text;

    public string text
    {
        get => m_Text;
        set
        {
            m_Text = value;
            if (!Content) return;
            Content.text = m_Text;
        }
    }

    public Text Text => Content;

    public RectTransform Rect { get; private set; }

    private void Awake()
    {
        InitFsm();

        Rect = transform.GetComponent<RectTransform>();
        m_MaxWidth = Rect.sizeDelta.x;
        if (m_Value > 0 && Handler != null)
        {
            var tmpWidth = m_MaxWidth * HandlerFactor;
            Handler.transform.SetLocalPositionX(tmpWidth * m_Value - tmpWidth / 2);
        }
    }

    public void InitDrag(Action<PointerEventData> dragEvt)
    {
        onDragEvt = dragEvt;
        if (Handler != null)
        {
            var img = Handler.GetComponentInChildren<Image>();
            if (img != null)
            {
                var dragCmp = img.GetComponent<UGUIDragSupport>();
                if (dragCmp != null)
                {
                    dragCmp.onDragEvt = OnDrag;
                }
            }
        }
    }

    public void OnDrag(PointerEventData eventData)
    {
        var worldPos = eventData.pointerCurrentRaycast.worldPosition;
        var p = Handler.transform.position;
        Handler.transform.SetPositionX(worldPos.x);
        var localX = Handler.transform.localPosition.x;
        var tmpWidth = m_MaxWidth * HandlerFactor;
        var val = (localX + tmpWidth / 2) / tmpWidth;
        Value = Mathf.Clamp01(val);
        onDragEvt?.Invoke(eventData);
    }

    private void OnDisable()
    {
        StopCountDown();
        StopProgressAnim();
    }

    /// <summary>
    /// ÉèÖÃ¿í¶ÈºÍ¸ß¶È
    /// </summary>
    /// <param name="vec2"></param>
    public void SetSizeDelta(Vector2 vec2)
    {
        Rect.sizeDelta = vec2;
        m_MaxWidth = Rect.sizeDelta.x;
    }

    /// <summary>
    /// ÓÃ¿í¶È»»Ëã³ö Value
    /// </summary>
    public void SetWidth(float width)
    {
        var val = width / m_MaxWidth;
        Value = val;
    }

    public void SetPro(long curValue, long maxValue)
    {
        Value = (float)curValue / maxValue;
        text = curValue.ToString() + "/" + maxValue.ToString();
    }

    public void SetProPercent(long curValue, long maxValue)
    {
        Value = (float)curValue / maxValue;
        text = curValue.ToString() + "%/" + maxValue.ToString() + "%";
    }

    /// <summary>
    /// ÉèÖÃÍ¼Æ¬¸ñÊ½
    /// </summary>
    /// <param name="type">Í¼Æ¬Ê¹ÓÃÀàÐÍ normal ,slice, fill µÈ</param>
    /// <param name="fillMethod">Ë®Æ½»¹ÊÇ´¹Ö±</param>
    /// <param name="fillOrigin">0 Left  1 Right</param>
    public void SetProgressImgType(Image.Type type, Image.FillMethod fillMethod, int fillOrigin)
    {
        Progress.type = type;
        Progress.fillMethod = fillMethod;
        Progress.fillOrigin = fillOrigin;
    }

    /// <summary>
    /// ÔOÖÃµ×ˆD
    /// </summary>
    /// <param name="path"></param>
    public void SetBgSprite(string path)
    {
        if (BG == null)
        {
            BG = GetComponent<SuperImage>();
        }

        BG?.SetSprite(path);
    }

    /// <summary>
    /// ÔOÖÃßM¶ÈˆD
    /// </summary>
    /// <param name="path"></param>
    /// <param name="callback"></param>
    public void SetProgressSprite(string path, Action callback)
    {
        Progress.SetSprite(path, callback: callback);
    }

    public void SetTextColor(string colorHtml)
    {
        Content?.SetColor(colorHtml);
    }

    public void SetTextColor(Color color)
    {
        Content?.SetColor(color);
    }

    public void StopCountDown()
    {
        if (m_Coroutine != null)
        {
            StopCoroutine(m_Coroutine);
            m_Coroutine = null;
        }
    }

    public void CountDown(long totalTime, long leftTime, string content = "", Action completeCallback = null, TimeFormatType timeType = TimeFormatType.HH_MM_SS, TimeShowCount count = TimeShowCount.All)
    {
        m_TimeType = timeType;
        m_TotalTime = totalTime;
        m_Content = content;
        m_TimeShowCount = count;

        if (m_Coroutine != null)
        {
            StopCoroutine(m_Coroutine);
        }
        m_Coroutine = StartCoroutine(Utility.Coroutine.CountDown(leftTime, UpdateTimer, completeCallback));
    }

    private void UpdateTimer(float elapsedTime)
    {
        Value = elapsedTime == 0 ? 0 : elapsedTime / m_TotalTime;
        text = m_Content.IsNullOrEmpty() ? elapsedTime.TimeToString(m_TimeType, m_TimeShowCount) : m_Content.Format(elapsedTime.TimeToString(m_TimeType));
    }

    #region ProgressAnimation

    private const float FULL_SLIDER_TIME = 0.4f;
    private const float LAST_SLIDER_TIME = 0.6f;

    private float m_TargetValue = 0;
    private float m_StartValue = 0;
    private float m_AddValue = 0;
    private float m_SpaceTime = 0;
    Coroutine m_AniCoroutine;

    SimpleFSM m_Fsm = new SimpleFSM();
    SimpleState idle = new SimpleState("idle");
    SimpleState play = new SimpleState("play");
    private void InitFsm()
    {
        m_Fsm.AddState(idle, OnIdleEnter);
        m_Fsm.AddState(play, OnPlayEnter, OnPlayUpdate, OnPlayExit);
    }

    private void OnIdleEnter()
    {
        if (m_AddValue <= 0)
        {
            m_EndCb?.Invoke();
            m_EndCb = null;
            m_SmoothCb = null;
            StopAnimCoroutine();
            return;
        }
        m_Fsm.SetState(play);
    }

    private void OnPlayEnter()
    {
        if ((m_AddValue + m_StartValue) > 1)
        {
            m_TargetValue = 1;
            m_AddValue -= (m_TargetValue - m_StartValue);
            m_SpaceTime = (m_TargetValue - m_StartValue) * FULL_SLIDER_TIME;
        }
        else
        {
            m_TargetValue = m_AddValue;
            m_AddValue = 0;
            m_SpaceTime = (m_TargetValue - m_StartValue) * LAST_SLIDER_TIME;
        }
    }

    private void OnPlayUpdate(float dt)
    {
        float curValue = Mathf.Lerp(m_StartValue, m_TargetValue, m_Fsm.stateTime / m_SpaceTime);
        Value = Mathf.Min(curValue, m_TargetValue);
        if (m_Fsm.stateTime >= m_SpaceTime)
            m_Fsm.SetState(idle);
    }

    private void OnPlayExit()
    {
        m_StartValue = 0;
        if (m_AddValue > 1)
            m_SmoothCb?.Invoke();
    }

    /// <summary>
    /// ²¥·Å½ø¶ÈÌõ¶¯»­
    /// </summary>
    /// <param name="addValue"></param>
    /// <param name="cb"></param>
    /// <param name="endCb"></param>
    public void PlaySmoothProgress(float addValue, Action cb, Action endCb)
    {
        m_AniCoroutine = StartCoroutine(Utility.Coroutine.Update(OnUpdate));
        m_SmoothCb = cb;
        m_EndCb = endCb;
        m_AddValue += addValue;
        if (m_Fsm.IsState(play))
            return;
        m_StartValue = m_Value;
        m_Fsm.SetState(idle, true);
    }

    private void OnUpdate()
    {
        m_Fsm.UpdateState(Time.deltaTime);
    }

    private void StopProgressAnim()
    {
        m_AddValue = 0;
        m_Fsm.SetState(idle);
        StopAnimCoroutine();
    }

    private void StopAnimCoroutine()
    {
        if (m_AniCoroutine != null)
        {
            StopCoroutine(m_AniCoroutine);
            m_AniCoroutine = null;
        }
    }


    #endregion

    /// <summary>
    /// 填充Tweem
    /// </summary>
    /// <param name="endValue"></param>
    /// <param name="startValue"></param>
    /// <param name="durTime"></param>
    /// <param name="delayTime"></param>
    /// <param name="callback"></param>
    public void DOFillAmount(float endValue, float startValue = 0, float durTime = 0.5f, float delayTime = 0f, Action callback = null)
    {
        this.Progress.DOKill();
        Value = startValue;
        this.Progress.DOFillAmount(endValue, durTime).SetDelay(delayTime).
        OnComplete(() =>
        {
            Value = endValue;
            callback?.Invoke();
        });
    }
}
